#pragma once

#include <gtl/type/inner.h>

class IInterface
{
public:
	enum { ID = 'IInt'|'erfa'|'ce' };

public:
	virtual HRESULT QueryInterface(int iid, void** ppvObject) = 0;

	virtual ULONG AddRef(void) { return 1; }

	virtual ULONG Release(void) { return 0; }

	template<class Interface>
	inline Interface* QueryInterface()
	{
		Interface* pInterface = NULL;
		QueryInterface(Interface::ID, (void**)&pInterface);
		return pInterface;
	}

	template<class Interface>
	inline Interface* QI()
	{
		return QueryInterface<Interface>();
	}
};

template<class Interface, class TBase = void, int id = Interface::ID>
class IInterfaceT : public Interface, public TBase
{
public:
	virtual HRESULT QueryInterface(int iid, void** ppvObject)
	{
		if(iid == id)
		{
			*ppvObject = static_cast<Interface*>(this);
			return S_OK;
		}

		return TBase::QueryInterface(iid, ppvObject);
	}
};

template<class Interface, int id>
class IInterfaceT<Interface, void, id> : public Interface
{
public:
	virtual HRESULT QueryInterface(int iid, void** ppvObject)
	{
		if(iid == id)
		{
			*ppvObject = static_cast<Interface*>(this);
			return S_OK;
		}

		return E_NOINTERFACE;
	}
};

template<class I1 = void, class I2 = void, 
		 class I3 = void, class I4 = void, 
		 class I5 = void, class I6 = void, 
		 class I7 = void, class I8 = void, 
		 class I9 = void, class I10 = void
		>
class IMultiInterface;

template<class I1>
class IMultiInterface<I1, void, void, void, void, void, void, void, void, void>
	: public IInterfaceT<I1>
{
public:
	virtual ~IMultiInterface() {}
};

template<class I1, class I2>
class IMultiInterface<I1, I2, void, void, void, void, void, void, void, void>
	: public IInterfaceT<I2, IMultiInterface<I1> >
{
public:
	virtual ~IMultiInterface() {}
};

template<class I1, class I2, class I3>
class IMultiInterface<I1, I2, I3, void, void, void, void, void, void, void>
	: public IInterfaceT<I3, IMultiInterface<I1, I2> >
{
public:
	virtual ~IMultiInterface() {}
};

template<class I1, class I2, class I3, class I4>
class IMultiInterface<I1, I2, I3, I4, void, void, void, void, void, void>
	: public IInterfaceT<I4, IMultiInterface<I1, I2, I3> >
{
public:
	virtual ~IMultiInterface() {}
};

template<class I1, class I2, class I3, class I4, class I5>
class IMultiInterface<I1, I2, I3, I4, I5, void, void, void, void, void>
	: public IInterfaceT<I5, IMultiInterface<I1, I2, I3, I4> >
{
public:
	virtual ~IMultiInterface() {}
};

template<class I1, class I2, class I3, class I4, class I5, class I6>
class IMultiInterface<I1, I2, I3, I4, I5, I6, void, void, void, void>
	: public IInterfaceT<I6, IMultiInterface<I1, I2, I3, I4, I5> >
{
public:
	virtual ~IMultiInterface() {}
};

template<class I1, class I2, class I3, class I4, class I5, class I6, class I7>
class IMultiInterface<I1, I2, I3, I4, I5, I6, I7, void, void, void>
	: public IInterfaceT<I7, IMultiInterface<I1, I2, I3, I4, I5, I6> >
{
public:
	virtual ~IMultiInterface() {}
};

template<class I1, class I2, class I3, class I4, class I5, class I6, class I7, class I8>
class IMultiInterface<I1, I2, I3, I4, I5, I6, I7, I8, void, void>
	: public IInterfaceT<I8, IMultiInterface<I1, I2, I3, I4, I5, I6, I7> >
{
public:
	virtual ~IMultiInterface() {}
};

template<class I1, class I2, class I3, class I4, class I5, class I6, class I7, class I8, class I9>
class IMultiInterface<I1, I2, I3, I4, I5, I6, I7, I8, I9, void>
	: public IInterfaceT<I9, IMultiInterface<I1, I2, I3, I4, I5, I6, I7, I8> >
{
public:
	virtual ~IMultiInterface() {}
};
